﻿#include <unordered_map>
#include <vector>
#include <commands.h>
#include <modules.h>
#include <cliManager.h>
#include <log.h>
#include <validator.h>
#include <template.h>
#include <clibase.h>
#include <cnotify.h>
#include <timerManager.h>
#include <keyMap.h>
#include <framework.h>
#include <jobQueue.h>

LOCAL const cli::CliCmd g_cli_commands[] = 
{
    {
        "ver",
        "Display system version information",
        MODULE_CLI_BASE,
        CMD_SYS_VER,
    },
    {
        "exit",
        "Terminate current user session",
        MODULE_CLI_BASE,
        CMD_USER_EXIT,
    },
    {
        "shutdown",
        "Shutdown the entire system",
        MODULE_CLI_BASE,
        CMD_SYS_SHUTDOWN,
    },
    {
        "help",
        "Show help information for specific command",
        MODULE_CLI_BASE,
        CMD_SYS_HELP,
    },
    {
        "?",
        "Show help information for specific command",
        MODULE_CLI_BASE,
        CMD_SYS_HELP,
    },
    {
        "dump",
        "Dump detailed information from specified module",
        MODULE_CLI_BASE,
        CMD_SYS_DUMP,
        {
            {
                "module",
                "m",
                "Module id to dump information",
                false,
                NULLSTR,
                true,
                nullptr
            }
        }
    },
    {
        "set-log-level",
        "Set log level for specified module",
        MODULE_CLI_BASE,
        CMD_SET_LOG_LEVEL,
        {
            {
                "module",
                "m",
                "module id",
                true,
                "0",
                true,
                nullptr
            },
            {
                "level",
                "l",
                "log level (trace/debug/info/warn/error/fatal)",
                true,
                "info",
                true,
                parser::enum_validator<int>(
                    log_check_string,
                    {
                        LOG_LVL_TRACE,
                        LOG_LVL_DEBUG,
                        LOG_LVL_INFO,
                        LOG_LVL_WARN,
                        LOG_LVL_ERROR,
                        LOG_LVL_FATAL
                    }
                )
            }
        }
    },
    {
        "get-log-level",
        "Get current log level of specified module",
        MODULE_CLI_BASE,
        CMD_GET_LOG_LEVEL,
        {
            {
                "module",
                "m",
                "module id",
                true,
                "0",
                true,
                nullptr
            }
        }
    },
    {
        "set-log-trace",
        "Enable or disable trace logging for specified module",
        MODULE_CLI_BASE,
        CMD_SET_LOG_TRACE,
        {
            {
                "module",
                "m",
                "module id",
                true,
                "0",
                true,
                nullptr
            },
            {
                "status",
                "s",
                "trace status (on/off)",
                true,
                "off",
                true,
                parser::oneof<std::string>("on", "off")
            }
        }
    },
    {
        "get-log-trace",
        "Get current trace logging status of specified module",
        MODULE_CLI_BASE,
        CMD_GET_LOG_TRACE,
        {
            {
                "module",
                "m",
                "module id",
                true,
                "0",
                true,
                nullptr
            }
        }
    },
    {
        "get-log-list",
        "Get log instances of all modules",
        MODULE_CLI_BASE,
        CMD_GET_LOG_LIST,
    }
};


STATUS clibase::dump(TextTable &t)
{
    return OK;
}
STATUS clibase::objectDump(const char* name, TextTable &outMsg)
{
    outMsg.setTableName("Module List");
    outMsg.addHeader("objId");
    outMsg.addHeader("module");
    outMsg.addHeader("objPtr").endOfRow();
    std::lock_guard<std::mutex> lock(GET_FRAMEWORK_MGR->m_objectLock);
    for (auto& iter : GET_FRAMEWORK_MGR->m_objectList)
    {
        outMsg.add(utils::to_hexstring(iter.second->objectId));
        outMsg.add(iter.second->objectName);
        outMsg.add(utils::to_hexstring(iter.second->objectPtr)).endOfRow();
    }

    return OK;
}
STATUS clibase::printHelp(TextTable &outMsg)
{
    auto funcHelp = [](cli::cliManager* pCli, TextTable& outMsg)
        {
            outMsg.setTableName("Support commands");
            outMsg.addHeader("name");
            outMsg.addHeader("receiver");
            outMsg.addHeader("opcode");
            outMsg.addHeader("description").endOfRow();

            std::lock_guard<std::mutex> lock(pCli->m_objectLock);
            for (auto& cmd : pCli->m_cmdList)
            {
                outMsg.add(cmd.first);
                outMsg.add(utils::to_hexstring(cmd.second->pCmdDesc->recvId));
                outMsg.add(utils::to_hexstring(cmd.second->pCmdDesc->cmdId));
                outMsg.add(cmd.second->pCmdDesc->description).endOfRow();
            }
        };

    GET_FRAMEWORK_MGR->callObjectMethod<cli::cliManager>(MODULE_CLI_MGR, funcHelp, outMsg);

    return OK;
}
PROCESS_IMPL_DESC(clibase, process)
{
    PROCESS_BEGIN()
    PROCESS_CALL(CMD_SYS_VER, GetVersion)
    PROCESS_CALL(CMD_SYS_SHUTDOWN, SysShutDown)
    PROCESS_CALL(CMD_SYS_HELP, SysHelp)
    PROCESS_CALL(CMD_SYS_DUMP, SysDump)
    PROCESS_CALL(CMD_SET_LOG_LEVEL, SetLogLevel)
    PROCESS_CALL(CMD_GET_LOG_LEVEL, GetLogLevel)
    PROCESS_CALL(CMD_SET_LOG_TRACE, SetLogTrace)
    PROCESS_CALL(CMD_GET_LOG_TRACE, GetLogTrace)
    PROCESS_CALL(CMD_GET_LOG_LIST,  GetLogList)
    PROCESS_END(OK)
}
LOCAL void logforeach(TextTable* outMsg, const Ilog::LogMap::value_type& logInst)
    {
        outMsg->add(utils::format("%u", logInst.first));
        outMsg->add(logInst.second->getLogName());
        outMsg->add(log_to_string(logInst.second->getLogLevel()));
        outMsg->add(switch_to_string(logInst.second->getLogTraceDebug()));
        outMsg->endOfRow();
    };

PROCESS_IMPL_DESC(clibase, GetLogList)
{
    outMsg.setTableName("Log module Information");
    outMsg.addHeader("Id");
    outMsg.addHeader("file");
    outMsg.addHeader("Level");
    outMsg.addHeader("Trace");
    outMsg.endOfRow();
    Ilog::forEachLogModule((FUNCPTR)(logforeach), (void*) &outMsg);
    return OK;
}

PROCESS_IMPL_DESC(clibase, GetVersion)
{
    outMsg.setTableName("System Information");
    outMsg.addHeader("Version");
    outMsg.addHeader("BuildTime");
    outMsg.endOfRow();
    outMsg.add(FrameWorkMgr::getVersion());
    outMsg.add(FrameWorkMgr::getBuildTime());
    outMsg.endOfRow();

    return OK;
}
LOCAL void SysShutDownReport()
{
    TextTable outMsg;

    outMsg.setTableName("System ShutDown");
    outMsg.addHeader("Alert message");
    outMsg.endOfRow();
    outMsg.add("System shutdown in 5 seconds!");
    outMsg.endOfRow();

    LVOS_Report(outMsg);
    GET_FRAMEWORK_MGR->callObjectMethod<Cnotify, STATUS>(
        MODULE_NOTIFY,
        &Cnotify::broadcast,
        static_cast<int>(MODULE_CLI_BASE),
        static_cast<int>(CMD_SYS_SHUTDOWN),
        static_cast<int>(MODULE_ALL),
        static_cast<bool>(true)
    );

    GET_FRAMEWORK_MGR->setExit();
}

PROCESS_IMPL_DESC(clibase, SysShutDown)
{
    return jobAdd(reinterpret_cast<VOIDFUNCPTR>(SysShutDownReport), nullptr);
}

PROCESS_IMPL_DESC(clibase, SysHelp)
{
    return printHelp(outMsg);
}

PROCESS_IMPL_DESC(clibase, SysDump)
{
    if (reqArgs.has("module"))
    {
        int objId = utils::to_Signed<int>(reqArgs.get("module"), -1);
        return GET_FRAMEWORK_MGR->callObjectMethod<objbase, STATUS>(objId, &objbase::dump, outMsg);
    }
    else
    {
        return objectDump(reqArgs.get("module"), outMsg);
    }
}
PROCESS_IMPL_DESC(clibase, SetLogLevel)
{
    COND_CHECK_RET_ERROR(!reqArgs.has("module"));

    int logInst = utils::to_Signed<int>(reqArgs.get("module"), 0);
    int level = LOG_LVL_INFO;

    level = log_from_string(reqArgs.get("level"));

    auto logIter = Ilog::logModuleGet(logInst);
    COND_CHECK_RET_ERROR(logIter == nullptr);

    return logIter->setLogLevel(level);
}

PROCESS_IMPL_DESC(clibase, GetLogLevel)
{
    COND_CHECK_RET_ERROR(!reqArgs.has("module"));

    int logInst = utils::to_Signed<int>(reqArgs.get("module"), 0);

    auto logIter = Ilog::logModuleGet(logInst);
    COND_CHECK_RET_ERROR(logIter == nullptr);

    int level = logIter->getLogLevel();

    outMsg.setTableName("Log Information");
    outMsg.addHeader("module");
    outMsg.addHeader("level").endOfRow();

    outMsg.add(std::to_string(logInst));
    outMsg.add(log_to_string(level)).endOfRow();

    return OK;
}
PROCESS_IMPL_DESC(clibase, SetLogTrace)
{
    COND_CHECK_RET_ERROR(!reqArgs.has("module"));

    int logInst = utils::to_Signed<int>(reqArgs.get("module"), 0);

    auto logIter = Ilog::logModuleGet(logInst);
    COND_CHECK_RET_ERROR(logIter == nullptr);

    return logIter->setLogTraceDebug(switch_from_string(reqArgs.get("status")));
}
PROCESS_IMPL_DESC(clibase, GetLogTrace)
{
    COND_CHECK_RET_ERROR(!reqArgs.has("module"));

    int logInst = utils::to_Signed<int>(reqArgs.get("module"), 0);

    auto logIter = Ilog::logModuleGet(logInst);
    COND_CHECK_RET_ERROR(logIter == nullptr);

    outMsg.setTableName("Log Traace Information");
    outMsg.addHeader("module");
    outMsg.addHeader("status").endOfRow();

    outMsg.add(std::to_string(logInst));
    outMsg.add(switch_to_string(logIter->getLogTraceDebug()));
    outMsg.endOfRow();

    return OK;
}
STATUS clibase::preInit()
{
    STATUS retVal = OK;

    retVal = registerMessageListener(MODULE_CLI_BASE);
    COND_CHECK_RET_ERROR(retVal != OK);

    retVal = GET_FRAMEWORK_MGR->callObjectMethod<cli::cliManager, STATUS>
        (MODULE_CLI_MGR, &cli::cliManager::cliCmdRegister, &g_cli_commands[0], size_t(NELEMENTS(g_cli_commands)));
    COND_CHECK_RET_ERROR(retVal != OK);
    

    TimerManager::Callback timer5sTask = []()->void {
        GET_FRAMEWORK_MGR->callObjectMethod<Cnotify, STATUS>(
            MODULE_NOTIFY,
            &Cnotify::broadcast,
            static_cast<int>(MODULE_CLI_BASE),
            static_cast<int>(CMD_MSG_TIMER_5S),
            static_cast<int>(MODULE_ALL),
            static_cast<bool>(true)
        );
        };

    retVal = GET_FRAMEWORK_MGR->callObjectMethod<TimerManager, STATUS>(
        MODULE_TIMER_MGR,
        &TimerManager::addTimer,
        static_cast<const std::string&>("timer5s"),
        static_cast<unsigned int>(5),
        static_cast<TimerManager::Callback>(timer5sTask)
    );

    COND_CHECK_RET_ERROR(retVal != OK);

    return OK;
}
LOCAL STATUS cliUserExitProc(cli::CliCmd* pCmd, SessionPtr session)
{
    if (pCmd->cmdId == CMD_USER_EXIT)
    {
        session->close();
        return OK;
    }
    return RET_ERROR;
}

STATUS clibase::postInit()
{
    return GET_FRAMEWORK_MGR->callObjectMethod<cli::cliManager, STATUS>
        (MODULE_CLI_MGR, &cli::cliManager::cliSetDispatchHook, reinterpret_cast<FUNCPTR>(cliUserExitProc));
}

STATUS clibase::destory()
{
    return OK;
}
REG_TO_FRAMEWORK(clibase, MODULE_CLI_BASE);